/*
************************************************************************************************
*  File     : flash.c
*
*  Driver for NXP LPC17xx Flash.
*
*  Author   : zhangchao
*  Version  : 1.0
*
*
************************************************************************************************
*/
#include "LPC177x_8x.h"
#include "flash.h"

// IAP struct
static struct sIAP IAP;

// IAP Call
typedef void (*IAP_Entry) (uint32_t *cmd, uint32_t *stat);
#define IAP_Call ((IAP_Entry) 0x1FFF1FF1)


/*
***********************************************************************************************
*    uint32_t Flash_GetSecNum (uint32_t adr)
*
*    Get Sector Number
*    arguments : 1
*                uint32_t adr:  Sector Address
*    return    : Sector Number
**********************************************************************************************
*/
uint32_t Flash_GetSecNum(uint32_t adr)
{
	uint32_t n;
	
	n = adr >> 12;                              //  4kB Sector
	if(n >= 0x10)
	{
		n = 0x0E + (n >> 3);                   // 32kB Sector
	}
	
	return n;                                 // Sector Number
}

/*
***********************************************************************************************
*    void Flash_Init (uint32_t adr, uint32_t clk, uint32_t fnc)
*
*    Initialize Flash Programming Functions
*    Use for IAP mode's reinvoke ISP mode,
*    ISP mode needs the Internal RC Oscillator, disbale PLL, and in User Flash Mode
*    arguments : 3
* 				uint32_t adr:  Device Base Address
* 				uint32_t clk:  Clock Frequency (Hz)
*				uint32_t fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
*    return    : 0 - OK,  1 - Failed
**********************************************************************************************
*/
int32_t Flash_Init(uint32_t adr, uint32_t clk, uint32_t fnc)
{
	LPC_SC->PLL0CON  = 0x00;                        // Disable PLL (use Oscillator)
	LPC_SC->PLL0FEED = 0xAA;                       // Feed Sequence Part #1
	LPC_SC->PLL0FEED = 0x55;                      // Feed Sequence Part #2
	
	MEMMAP   = 0x01;                             // User Flash Mode

	return 0;
}

/*
***********************************************************************************************
*    int32_t Flash_UnInit (uint32_t fnc)
*
*    De-Initialize Flash Programming Functions
*    arguments : 1
*                uint32_t fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
*    return    : 0 - OK,  1 - Failed
**********************************************************************************************
*/
int32_t Flash_UnInit(uint32_t fnc)
{
	return 0;
}

/*
***********************************************************************************************
*    int32_t Flash_EraseChip (void)
*
*    Erase complete Flash Memory
*    arguments : NONE
*    return    : 0 - OK,  1 - Failed
**********************************************************************************************
*/
int32_t Flash_EraseChip(void)
{
	IAP.cmd    = PREPARE_WRITE;                             // Prepare Sector for Erase
	IAP.par[0] = 0;                              // Start Sector
	IAP.par[1] = END_SECTOR;                     // End Sector
	IAP_Call (&IAP.cmd, &IAP.stat);              // Call IAP Command
	if(IAP.stat)
		return (1);                              // Command Failed
	
	IAP.cmd    = EARSE_SECTOR;                             // Erase Sector
	IAP.par[0] = 0;                              // Start Sector
	IAP.par[1] = END_SECTOR;                     // End Sector
	IAP.par[2] = SystemCoreClock/4/1000;                           // CCLK in kHz
	IAP_Call (&IAP.cmd, &IAP.stat);              // Call IAP Command
	if(IAP.stat)
		return 1;                                // Command Failed
	
	return 0;                                  // Finished without Errors
}

/*
***********************************************************************************************
*    int32_t Flash_EraseSector (uint32_t adr)
*
*    Erase Sector in Flash Memory
*    arguments : 1
*                uint32_t adr:  Sector Address
*    return    : 0 - OK,  1 - Failed
**********************************************************************************************
*/
int32_t Flash_EraseSector(uint32_t adr)
{
	uint32_t n;
	
	n = Flash_GetSecNum(adr);                          // Get Sector Number
	
	IAP.cmd    = PREPARE_WRITE;                             // Prepare Sector for Erase
	IAP.par[0] = n;                              // Start Sector
	IAP.par[1] = n;                              // End Sector
	IAP_Call(&IAP.cmd, &IAP.stat);              // Call IAP Command
	if (IAP.stat)
		return 1;                                // Command Failed
	
	IAP.cmd    = EARSE_SECTOR;                             // Erase Sector
	IAP.par[0] = n;                              // Start Sector
	IAP.par[1] = n;                              // End Sector
	IAP.par[2] = SystemCoreClock/4/1000;                           // CCLK in kHz
	IAP_Call (&IAP.cmd, &IAP.stat);              // Call IAP Command
	if(IAP.stat)
		return 1;                                // Command Failed
	
	return 0;                                  // Finished without Errors
}

/*
***********************************************************************************************
*    int32_t Flash_ProgramPage (uint32_t adr, uint32_t sz, uint8_t *buf)
*
*    Program Page in Flash Memory
*    arguments : 3
* 				uint32_t adr:  Page Start Address
* 				uint32_t sz:   Page Size
*				uint8_t *buf:  Page Data
*    return    : 0 - OK,  1 - Failed
**********************************************************************************************
*/
int32_t Flash_ProgramPage(uint32_t adr, uint32_t sz, uint8_t *buf)
{
	uint32_t n;
	
#if SET_VALID_CODE != 0                        // Set valid User Code Signature
	if(adr == 0)
	{                              // Check for Vector Table
		n = *((uint32_t *)(buf + 0x00)) +
			*((uint32_t *)(buf + 0x04)) +
			*((uint32_t *)(buf + 0x08)) +
			*((uint32_t *)(buf + 0x0C)) +
			*((uint32_t *)(buf + 0x10)) +
			*((uint32_t *)(buf + 0x14)) +
			*((uint32_t *)(buf + 0x18));
				
		*((uint32_t *)(buf + 0x1C)) = 0 - n;  // Signature at Reserved Vector
	}
#endif
	
	n = Flash_GetSecNum(adr);                          // Get Sector Number
	
	IAP.cmd    = PREPARE_WRITE;                  // Prepare Sector for Write
	IAP.par[0] = n;                              // Start Sector
	IAP.par[1] = n;                              // End Sector
	IAP_Call(&IAP.cmd, &IAP.stat);              // Call IAP Command
	if(IAP.stat) 
		return 1;                                // Command Failed
	
	IAP.cmd    = CP_RAM_TO_FLASH;                // Copy RAM to Flash
	IAP.par[0] = adr;                            // Destination Flash Address
	IAP.par[1] = (uint32_t)buf;                  // Source RAM Address
	IAP.par[2] = 1024;                           // Fixed Page Size
	IAP.par[3] = SystemCoreClock/4/1000;                           // CCLK in kHz
	IAP.stat = 1;
	IAP_Call(&IAP.cmd, &IAP.stat);              // Call IAP Command
	if(IAP.stat)
		return 1;                                  // Command Failed
	
	return 0;                                  // Finished without Errors
}

